import { f, f_prevent } from './callback'
import { removeKeydown, removeKeyup } from './remove'
import { isDom, toKeyCode, toKeyOrKeyCode } from './utils'
import { dispatchKeyEvent } from './keyEvent'

class KeyboardMan {
  constructor() {
    this.i = 1
    this.cbMap = {}
    this.elMap = {}
  }

  onKeydown = function (
    cb,
    key,
    { modifiers = '', delay = 500, delayType = 'throttle', immediate = true, el = document } = {},
    ...args
  ) {
    if (isNaN(Number(delay))) {
      throw new Error(`AzKeyboardMan: Got an invalid delay: ${JSON.stringify(delay)}`)
    }

    if (!isDom(el)) el = document
    else el.tabIndex == null && (el.tabIndex = -1)

    const keyCode = toKeyCode(key)

    const func = f(cb, keyCode, modifiers, delay, delayType, immediate, ...args)

    el.addEventListener('keydown', func)
    this.cbMap[this.i] = func
    this.elMap[this.i] = el

    return this.i++
  }

  onKeyup = function (
    cb,
    key,
    { modifiers = '', delay = 500, delayType = 'throttle', immediate = true, el = document } = {},
    ...args
  ) {
    if (isNaN(Number(delay))) {
      throw new Error(`AzKeyboardMan: Got an invalid delay: ${JSON.stringify(key)}`)
    }

    if (!isDom(el)) el = document
    else el.tabIndex == null && (el.tabIndex = -1)

    const keyCode = toKeyCode(key)

    const func = f(cb, keyCode, modifiers, delay, delayType, immediate, ...args)
    const prevent = f_prevent(keyCode, modifiers)

    el.addEventListener('keydown', prevent)
    el.addEventListener('keyup', func)
    this.cbMap[this.i] = [prevent, func]
    this.elMap[this.i] = el

    return this.i++
  }

  onKeyRemove = function (i) {
    if (!i || !this.cbMap[i] || !this.elMap[i]) return false
    return Array.isArray(this.cbMap[i])
      ? removeKeyup(i, this.cbMap, this.elMap)
      : removeKeydown(i, this.cbMap, this.elMap)
  }
}

export function createKeyboardMan() {
  return new KeyboardMan()
}

export { toKeyOrKeyCode, dispatchKeyEvent }
